/*
 *  mih_service_management.c
 *  MIHF
 *
 *  Created by injae song on 11. 3. 4..
 *  Copyright 2011 KyungHee Univ. All rights reserved.
 *
 */

#include "mih_service_management.h"

void mih_service_management_dispatch( mih_message* msg,   mih_user *from_user)
{
	warn("Service management dispatch\n");
	
    mihf *mih = from_user->mih;
	
    char *dst_mihfid = get_dst_mihfid(msg);
	
    mih_message* confirm;
    
    if(dst_mihfid == NULL)
    {
        warn("No MIH destination\n");
        return;
    }
    
	warn("MIH destination is %s\n", dst_mihfid); //remote case
		
    if(strcmp(dst_mihfid, MIHF_ID) != 0) {	
        
        warn("MIH destination for remote Peer\n");
            
        mih_add_local_src_tlv(msg, mih);
            
        mih_send_event_subscribe_request(msg, from_user);
            
        return;
    } 
    else 
    {
        warn("MIH destination is local\n");
    
        switch(msg->header->aid) {
            case MIH_CAPABILITY_DISCOVER:
                switch(msg->header->opcode) 
                {
                    case REQUEST:
                        confirm = mih_capability_discover_request(msg,from_user->mih, from_user);
                        mih_sap_send_message(confirm, from_user);
					break;
                    
                    default :
                        break;
                }
                break;
            
            case MIH_REGISTER: //just skip...
                break;
                    
            case MIH_DEREGISTER://just skip...
                break;
	
            case MIH_EVENT_SUBSCRIBE:
                switch(msg->header->opcode) 
                {
                    case REQUEST:
                        confirm=mih_event_subscribe_request(msg, from_user);
                        mih_sap_send_message(confirm, from_user);
					break;
                    
                    default:
					break;
                }
			break;
		
            case MIH_EVENT_UNSUBSCRIBE:
                    confirm=mih_event_unsubscribe_request(msg, from_user);
                    mih_sap_send_message(confirm, from_user);
                break;
		
            default:
                break;
        }
    }

	return;

}
mih_message* mih_event_unsubscribe_request(mih_message *msg, mih_user* user)
{
    STATUS status = Success;
    
    generic_tlv *tlv;
    
    mih_message *confirm = new_mih_message(SERVICE_MANAGEMENT, CONFIRM, MIH_EVENT_UNSUBSCRIBE);
	
    warn("MIH local event subscribe\n");
    
	tlv = get_tlv(msg, MIH_EVT_LIST_TYPE);
	
	MIH_EVT_LIST eventbitfield = 0;
	
    memcpy(&eventbitfield, tlv->value, tlv->data_payload_length);
    
	mih_event_unsubscribe_request(msg, user);
    
    tlv = new_tlv(STATUS_TYPE,(char*) status , sizeof(STATUS));
    mih_message_add_tlv(confirm, tlv);
    
    tlv = new_tlv(MIH_EVT_LIST_TYPE,(char*) user->mih->supported_events , sizeof(STATUS));
    mih_message_add_tlv(confirm, tlv);
    
    return confirm;
}

mih_message* mih_event_subscribe_request(mih_message *msg, mih_user* user) 
{
	generic_tlv *tlv;
    
	warn("MIH local event subscribe\n");
    
	tlv = get_tlv(msg, MIH_EVT_LIST_TYPE);
	mih_message *confirm = new_mih_message(SERVICE_MANAGEMENT, CONFIRM, MIH_EVENT_UNSUBSCRIBE);
	unsigned long eventbitfield = 0;
	
    memcpy(&eventbitfield, tlv->value, tlv->data_payload_length);
	
	if(ISSET(MIH_LINK_DETECTED, eventbitfield))
		mih_events_subscribe(MIH_LINK_DETECTED, mih_user_event_indication, user); 
	if(ISSET(MIH_LINK_UP, eventbitfield))
		mih_events_subscribe(MIH_LINK_UP, mih_user_event_indication, user); 
	if(ISSET(MIH_LINK_DOWN, eventbitfield))
		mih_events_subscribe(MIH_LINK_DOWN, mih_user_event_indication, user); 
	if(ISSET(MIH_LINK_PARAMETERS_REPORT, eventbitfield))
		mih_events_subscribe(MIH_LINK_PARAMETERS_REPORT, mih_user_event_indication, user); 
	if(ISSET(MIH_LINK_GOING_DOWN, eventbitfield))
		mih_events_subscribe(MIH_LINK_GOING_DOWN, mih_user_event_indication, user); 
	if(ISSET(MIH_LINK_HANDOVER_IMMINENT, eventbitfield))
		mih_events_subscribe(MIH_LINK_HANDOVER_IMMINENT, mih_user_event_indication, user); 
	if(ISSET(MIH_Link_Handover_Complete, eventbitfield))
		mih_events_subscribe(MIH_Link_Handover_Complete, mih_user_event_indication, user); 
	if(ISSET(MIH_Link_PDU_Transmit_Status, eventbitfield))
		mih_events_subscribe(MIH_Link_PDU_Transmit_Status, mih_user_event_indication, user); 
    
    return confirm;
}

void mih_events_subscribe(MIH_EVT_LIST ev_type, mih_event_callback cb, void *cb_arg) {
	
	mih_event *_ev;
	
    mih_event_indication_callback *callback;
	
    mih_user *user =(mih_user*) cb_arg; 
	
	_ev = malloc(sizeof(mih_event));
	
    callback=_ev->callback;
	callback = malloc(sizeof(mih_event_indication_callback));
	callback->cb = cb;
	callback->cb_arg = cb_arg;
	
	list_ins_next(&user->mih->Event_Service, list_tail(&user->mih->Event_Service), (void*) _ev);
	
	return;
}

void mih_send_event_subscribe_request(mih_message *msg,  mih_user *from_user)  
{
    
    mih_protocol_transmit(msg, mih_event_subscribe_confirm, (void *) from_user, from_user->mih);
    
}

void mihf_peer_send_event_indication(mih_message *message, void *mihf_peer, void* mih) {
	
    struct _mihf_peer *peer = (struct _mihf_peer*) mihf_peer;
	char *mih_frame;
	
	mih_add_local_src_tlv(message, peer->mih);
	mih_add_dst_tlv(message, peer->mih_id, peer->mih);
    
	mih_frame = mih_message_serialize(message);
	if(peer) {
		mih_protocol_transmit(message, NULL, NULL, peer->mih);
	}
}


void mih_event_subscribe_confirm(int status, mih_message *msg, void *user) 
{
    
    mih_user *to_user = user;	
	
    warn("event subscribe confirm\n");
	
	mih_event_subscription_forward_setup(msg, user);
	
    mih_sap_send_message(msg, to_user);

}



void mih_event_subscription_forward_setup(mih_message *msg, void *user) {
	
    warn("Setup forwarding for remote events\n");
	
    mih_user *to_user = user;
    
	generic_tlv *tlv;
	
    tlv = get_tlv(msg, MIH_EVT_LIST_TYPE);
	
    unsigned long eventbitfield = 0;
	
    memcpy(&eventbitfield, tlv->value, tlv->data_payload_length);
	
	if(ISSET(MIH_LINK_DETECTED, eventbitfield))
        mih_events_subscribe(MIH_LINK_DETECTED, mih_user_notify_event, to_user); 
	if(ISSET(MIH_LINK_UP, eventbitfield))
        mih_events_subscribe(MIH_LINK_UP, mih_user_notify_event, to_user); 
	if(ISSET(MIH_LINK_DOWN, eventbitfield))
        mih_events_subscribe(MIH_LINK_DOWN, mih_user_notify_event, to_user); 
	if(ISSET(MIH_LINK_PARAMETERS_REPORT, eventbitfield))
        mih_events_subscribe(MIH_LINK_PARAMETERS_REPORT,mih_user_notify_event , to_user); 
	if(ISSET(MIH_LINK_GOING_DOWN, eventbitfield))
        mih_events_subscribe(MIH_LINK_GOING_DOWN, mih_user_notify_event, to_user); 
	if(ISSET(MIH_LINK_HANDOVER_IMMINENT, eventbitfield))
        mih_events_subscribe(MIH_LINK_HANDOVER_IMMINENT, mih_user_notify_event, to_user); 
	if(ISSET(MIH_Link_Handover_Complete, eventbitfield))
        mih_events_subscribe(MIH_Link_Handover_Complete, mih_user_notify_event, to_user); 
	if(ISSET(MIH_Link_PDU_Transmit_Status, eventbitfield))
        mih_events_subscribe(MIH_Link_PDU_Transmit_Status, mih_user_notify_event, to_user); 
}

void mih_user_notify_event(mih_message *message, void *_user) {

	mih_user *user = _user;
	
	char *mih_frame;
	
	mih_frame = mih_message_serialize(message);
	
    bufferevent_write(user->ev_buf, mih_frame, MIH_HEADER_SIZE + message->header->payloadlength);
    
}

mih_message* mih_capability_discover_request(mih_message *msg, mihf* mih, mih_user* user)
{
    STATUS status;
    
    generic_tlv *tlv;
    
	warn("MIH_Capability_Discover.Request\n");
    
    mih_message *confirm = new_mih_message(SERVICE_MANAGEMENT, CONFIRM, MIH_CAPABILITY_DISCOVER);
    
	tlv = get_tlv(msg, MIH_EVT_LIST_TYPE);
	if(tlv!=NULL)
    {
        tlv = new_tlv(MIH_EVT_LIST_TYPE,(char*) &mih->supported_events , sizeof(mih->supported_events));
        mih_message_add_tlv(confirm, tlv);
    }
    
    tlv = get_tlv(msg, MIH_CMD_LIST_TYPE);
	if(tlv!=NULL)
    {
        tlv = new_tlv(MIH_CMD_LIST_TYPE,(char*) &mih->supported_cmds , sizeof(mih->supported_cmds));
        mih_message_add_tlv(confirm, tlv);
    }
    
    tlv = get_tlv(msg, MIH_IQ_TYPE_LST_TYPE);
    if(tlv!=NULL)
    {
        tlv = new_tlv(MIH_CMD_LIST_TYPE,(char*) &mih->supported_isqueries , sizeof(mih->supported_isqueries));
        mih_message_add_tlv(confirm, tlv);
    }
    
    tlv = new_tlv(STATUS_TYPE,(char*) status , sizeof(STATUS));
    mih_message_add_tlv(confirm, tlv);
    
    return confirm;
}